import numpy as np
import pandas as pd
from tqdm import tqdm
import matplotlib.pyplot as plt
import statsmodels.api as sm
from collections import Counter
import seaborn as sns
import gzip
import logging

class SimpleAnalysis(object):

    def __init__(self, factor, ret, freq, id):
        
        self.factor = factor
        self.ret = ret
        self.freq = freq
    def __init__(self, factor, ret, id):
        
        self.ret = ret
        self.factor = factor
        # self.freq = freq
        self.id = id


    def IC_ana(self,type):
        IC = self.factor.corrwith(self.ret, axis = 1, method = type).dropna() 
        return IC

    def ana_IC_indicator(self, type):
        
        # 计算整体IC指标，包括IC_mean、IC_std、IC_IR
        IC_Series = self.IC_ana(type)
        IC_Series.name = type
        # ic_id = ['ic_mean', 'ic_std', 'ic_ir']
        ic_mean = IC_Series.mean()
        ic_std = IC_Series.std()
        ic_ir = ic_mean / ic_std
        ic_indicators = pd.Series({'ic_mean': ic_mean, 'ic_std': ic_std, 'ic_ir': ic_ir})
        ic_indicators.name = type
        display(ic_indicators)


        # 按年度计算IC指标
        IC_Series.index = pd.to_datetime(IC_Series.index)
        year_mean = IC_Series.groupby(IC_Series.index.year).mean()
        year_std = IC_Series.groupby(IC_Series.index.year).std()
        year_ir = year_mean / year_std

        # 计算20天IC移动平均值、累计IC，用以画图
        # ic_20d_avg = IC_Series.rolling(20,min_periods = 1).mean()
        ic_cum = IC_Series.cumsum()

        fig, ax1 = plt.subplots(figsize=(18,6))  
        ax2 = ax1.twinx()  
        ax1.plot(IC_Series, label='ic',color='y') 
        # ax1.plot(ic_20d_avg, label='ic 22 days moving mean',color='g')  
        ax1.set_ylabel('ic',fontsize=15)  
        ax1.tick_params(axis='y', labelcolor='b')  
        ax1.grid()
        ax2.plot(ic_cum, label='ic_cum',color='b')  
        ax2.set_ylabel('ic_cum',fontsize=15) 
        ax2.tick_params(axis='y', labelcolor='r')  
        ax2.set_xticks(IC_Series.index[::120])
        ax1.tick_params(axis='x', rotation=45)  # 设置 x 轴标签的旋转角度

        ax1.legend(loc='upper left') # 在第一个 Y 轴上添加图例
        ax2.legend(loc='upper right') # 在第二个 Y 轴上添加图例
        plt.xlabel('date',fontsize=15)
        plt.title( self.id + ' '+ type,fontsize=18) 
        plt.show()
        
        return year_mean, year_ir, ic_indicators


    def cal_quantile_daily(self,date):
        daily_factor = self.factor.loc[date]
        daily_ret = self.ret.loc[date] 
        iCodes = daily_factor.dropna().index.intersection(daily_ret.dropna().index)
        factor_values = daily_factor.reindex(iCodes)
        ret_values = daily_ret.reindex(iCodes)
        bins = pd.qcut(factor_values, 10 , labels=False, duplicates='drop') +1 
        quantiles = {}
        for bin in bins.unique() :
            quantiles[bin] = ret_values[bins == bin].mean()
        quantiles = pd.Series(quantiles)
        return quantiles.sort_index()


    def cal_quantile_ret(self):
        return_Series = {}
        common_date = self.factor.dropna(axis = 0, how = 'all').index.intersection(self.ret.dropna(axis = 0, how = 'all').index)
        for date in tqdm(common_date):
            return_Series[date] = self.cal_quantile_daily(date)
        return_Series = pd.DataFrame(return_Series)
        return return_Series.T
        

    def quantile_ana(self, ret_portfolio):

        if self.freq == '1D':
            indicater_id = ['总收益 (%)','日均收益 (%)', '年化收益 (%)','年化波动率 (%)','夏普比率','卡玛比率','最大回撤 (%)','最长回撤']
            # indicator = []
            all_return = ret_portfolio.iloc[-1] - 1
            year_return = (ret_portfolio.iloc[-1]) ** (252 / ret_portfolio.shape[0]) -1
            day_vol = (ret_portfolio).pct_change().dropna().std()
            year_vol = day_vol * np.sqrt(252)
        elif self.freq == '10D':
            indicater_id = ['总收益 (%)','10日均收益 (%)', '年化收益 (%)','年化波动率 (%)','夏普比率','卡玛比率','最大回撤 (%)','最长回撤']
            all_return = ret_portfolio.iloc[-1] - 1
            year_return = (ret_portfolio.iloc[-1]) ** (24 / ret_portfolio.shape[0]) -1
            day_vol = (ret_portfolio).pct_change().dropna().std()
            year_vol = day_vol * np.sqrt(24)
        elif self.freq == '20D':
            indicater_id = ['总收益 (%)','20日均收益 (%)', '年化收益 (%)','年化波动率 (%)','夏普比率','卡玛比率','最大回撤 (%)','最长回撤']
            all_return = ret_portfolio.iloc[-1] - 1
            year_return = (ret_portfolio.iloc[-1]) ** (12 / ret_portfolio.shape[0]) -1
            day_vol = (ret_portfolio).pct_change().dropna().std()
            year_vol = day_vol * np.sqrt(12)
        else:
            raise ValueError('please input nD')

        #日均收益
        # meanReturn = dayReturn.mean()/period*100.0  #平均
        meanReturn = (ret_portfolio.iloc[-1] ** (1./ret_portfolio.shape[0]) - 1) 
    def cal_quantile_daily(self, date, n):
        #找出每个series的共同索引
        factor_series=self.factor.loc[date]
        ret_series=self.ret.loc[date]
        iCode=factor_series.dropna().index.intersection(ret_series.dropna().index)
        factor=factor_series.reindex(iCode)
        ret=ret_series.reindex(iCode)
        #找到n分层
        bins = pd.qcut(factor, q=n, labels=False, duplicates='drop')+1
        quantile_data={}
        for bin in bins.unique():
            quantile_data[bin]=ret[bins==bin].mean()
        quantile_data=pd.Series(quantile_data)
        #返回每层的ret
        return quantile_data.sort_index()
    
    def cal_quantile(self,n):
        commom_date=self.factor.dropna(how='all').index.intersection(self.ret.dropna(how='all').index)
        # print(commom_date)
        quantile_data={}
        for date in tqdm(commom_date):
            quantile_data[date]=self.cal_quantile_daily(date,n)
        quantile_data=pd.DataFrame(quantile_data).T
        return quantile_data
        

    def quantile_ana(self, quantile_data):

        ret_portfolio = (1+quantile_data).cumprod()
        indicater_id = ['总收益 (%)','日均收益 (%)', '年化收益 (%)','年化波动率 (%)','夏普比率','卡玛比率','最大回撤 (%)','最长回撤']
        all_return = ret_portfolio.iloc[-1] / ret_portfolio.iloc[0] - 1
        year_return = (1+quantile_data).prod() ** (252 / ret_portfolio.shape[0]) -1
        day_vol = (ret_portfolio).pct_change().dropna().std()
        year_vol = day_vol * np.sqrt(252)

        #日均收益
        meanReturn = ((1+quantile_data).prod() ** (1./ret_portfolio.shape[0]) - 1) 


        sharpe = year_return / year_vol if year_vol != 0 else 0

        max_cum = np.maximum.accumulate(ret_portfolio)
        drawback = 1 - ((ret_portfolio)) / max_cum
        max_drawback = np.max(drawback)

        carlmar = year_return / max_drawback

        longest_drawback = Counter(max_cum).most_common()[0][1]
        indicator = [all_return *100,meanReturn *100,  year_return *100, year_vol *100, sharpe, carlmar, max_drawback *100, longest_drawback]
        return (pd.Series(indicator, index = indicater_id))


    def cal_quantile_all_indicators_without_turnover(self):
        # 计算每日各分层收益
        factor_ret = self.cal_quantile_ret()
    def cal_quantile_all_indicators_without_turnover(self,n=10):
        # 计算每日各分层收益
        factor_ret = self.cal_quantile(n)
        factor_ret_mean = factor_ret.mean()

        # 画出日算术平均收益的柱状图
        plt.figure(figsize = (10,5))
        factor_ret_mean.plot(kind='bar')
        plt.grid()
        plt.title(self.id)
        plt.xlabel('quantile')
        plt.xticks(rotation=90)
        plt.ylabel('mean_return')
        plt.show()

        #计算各层累计收益风险指标
        factor_cum_ret = (factor_ret+1).cumprod()
        factor_cum_ret = factor_cum_ret.dropna(how='all')
        # factor_cum_ret['ni_top_bottom'] = ((factor_ret[10]-factor_ret[1])+1).cumprod()
        factor_quantiles_ana_result = pd.concat([
            self.quantile_ana(factor_cum_ret[x]) for x in factor_cum_ret.columns], axis = 1)
        factor_quantiles_ana_result = pd.concat([
            self.quantile_ana(factor_ret[x]) for x in factor_ret.columns], axis = 1)
        # factor_quantiles_ana_result.columns = list(factor_quantiles_ana_result.columns[:10] + 1) + ['10 - 1']
        factor_quantiles_ana_result.columns = list(factor_quantiles_ana_result.columns[:10] + 1)
        display(factor_quantiles_ana_result)


        # 画出累计收益图
        # all_indicator_quantile[id] = 
        factor_cum_ret = (factor_ret+1).cumprod()
        plt.figure(figsize = (20,5))
        plt.plot(factor_cum_ret)
        plt.grid()
        # plt.legend(list(range(1,11))+['top - bottom'])
        plt.legend(list(range(1,11)))
        plt.xticks(factor_cum_ret.index[::60])
        plt.title(self.id)
        plt.xlabel('date')
        plt.xticks(rotation=45)
        plt.ylabel('cum_return')
        plt.show()


    # 计算换手率
    def cal_turnover(self,num):
        bins = self.factor.apply(lambda x: pd.qcut(x, 10, labels= False, duplicates = 'drop'), axis = 1) +1
        fac_num = self.factor[bins == num]
        # commonfactor = up_factor[up_factor.notna() & ret.notna()]
        fac_num_shift = fac_num.shift(1)
        same_stocks = fac_num[fac_num_shift.notna()].count(axis = 1)
        stock_before = fac_num_shift.count(axis = 1)
        tr = 1 - same_stocks / stock_before
        tr.name = num
        return tr.fillna(0)
        

    def cal_quantile_all_indicators_with_turnover(self):
        factor_ret = self.cal_quantile_ret()

        turnovre_ratio = pd.concat([self.cal_turnover(num) for num in range(1,11)], axis = 1)

        # 画出第一层和最后一层的换手率
        plt.figure(figsize = (20,5))
        plt.plot(turnovre_ratio[[1,10]],label = ['1','10'])
        plt.grid()
        plt.title(self.id + ' turnover_ratio')
        plt.xticks(turnovre_ratio.index[::60])
        plt.xlabel('quantile')
        plt.xticks(rotation=45)
        plt.ylabel('turnover_ratio')
        plt.legend()
        plt.show()

        # 定义交易成本： 双边万三手续费+单边万五印花税
        trade_fee =  2 * turnovre_ratio * 0.0003 + 0.0005 * turnovre_ratio # + 0.0002
        # trade_fee = turnovre_ratio * 2 * 0.003 
        factor_ret_fee = factor_ret - trade_fee

        factor_ret_fee_mean = factor_ret_fee.mean()
        plt.figure(figsize = (10,5))
        factor_ret_fee_mean.plot(kind='bar')
        plt.grid()
        plt.title(self.id)
        plt.xlabel('quantile')
        plt.ylabel('mean_return')
        plt.show()

        #计算累计收益的收益风险指标
        factor_cum_ret = (factor_ret_fee+1).cumprod()
        factor_cum_ret = factor_cum_ret.dropna(how='all')
        factor_quantiles_ana_result = pd.concat([self.quantile_ana(factor_cum_ret[x]) for x in factor_cum_ret.columns], axis = 1)
        factor_quantiles_ana_result.columns = factor_quantiles_ana_result.columns[:10] + 1
        display(factor_quantiles_ana_result)

        # all_indicator_quantile[id] = quantile_ana(factor_cum_ret['ni_top_bottom'])
        plt.figure(figsize = (20,5))
        plt.plot(factor_cum_ret)
        plt.grid()
        plt.legend(list(range(1,11)))
        plt.xticks(factor_cum_ret.index[::60])
        plt.title(self.id)
        plt.xlabel('date')
        plt.xticks(rotation=45)
        plt.ylabel('cum_return')
        plt.show()
        return turnovre_ratio.mean()